package jfoo;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.io.IOException;
import java.io.PrintStream;
import java.util.Map;

import javax.sound.sampled.SourceDataLine;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JPanel;

import javazoom.jlgui.basicplayer.BasicController;
import javazoom.jlgui.basicplayer.BasicPlayer;
import javazoom.jlgui.basicplayer.BasicPlayerEvent;
import javazoom.jlgui.basicplayer.BasicPlayerException;
import javazoom.jlgui.basicplayer.BasicPlayerListener;
import jfoo.detailsTable.DetailsTable;
import jfoo.playlist.PlaylistListModel;
import kjdss.KJDigitalSignalSynchronizer;
import kjdss.ui.KJScopeAndSpectrumAnalyzer;
import net.roarsoftware.lastfm.Caller;
import net.roarsoftware.lastfm.scrobble.ResponseStatus;
import net.roarsoftware.lastfm.scrobble.Scrobbler;
import net.roarsoftware.lastfm.scrobble.Source;
import utils.FileUtil;
import audio.AudioFile;

public class ControlCenter extends JPanel implements BasicPlayerListener,
		KeyListener {

	JButton stop;

	JButton pause;

	JButton play;

	JButton previous;

	JButton next;

	JButton random;

	JButton open;

	Slider slider;

	DetailsTable detailsTable;
	PlaylistListModel playlistListModel;

	BasicPlayer player;

	BasicController control;

	private PrintStream out = null;

	private boolean paused = false;

	private Window window;

	private KJDigitalSignalSynchronizer dss = null;

	private KJScopeAndSpectrumAnalyzer wDsp;

	private AudioFile currentAudioFile = null;

	private Map audioInfo = null;

	private boolean iconified = false; // to stop displaying the spectrum when
	// the window is iconified

	private static String lastFMKey = "eda51fe4cdcb461a003ff25ac77ed58b";
	private boolean lastFmSubmitted = false;
	Scrobbler scrobbler = null;
	ResponseStatus status;

	public boolean isIconified() {
		return iconified;
	}

	public void setIconified(boolean iconified) {
		this.iconified = iconified;
	}

	public ControlCenter(Window window) {
		super();
		this.window = window;
		out = System.out;

		Caller.getInstance().setUserAgent("Jesuifoo");
		Caller.getInstance().setDebugMode(true);

		stop = new JButton(new ImageIcon("img/royal/stop.png"));
		stop.setToolTipText("Stop");
		stop.setSelectedIcon(new ImageIcon("img/royal/stop2.png"));
		stop.setFocusable(false);
		stop.setBorderPainted(false);
		stop.setPreferredSize(new Dimension(40, 35));
		// stop.setPreferredSize(new Dimension(20,20));

		// stop.setFocusPainted(false);
		// stop.setContentAreaFilled(false);

		pause = new JButton();
		pause.setIcon(new ImageIcon("img/royal/pause.png"));
		pause.setToolTipText("Pause");
		pause.setSelectedIcon(new ImageIcon("img/royal/pause2.png"));
		pause.setFocusable(false);
		// pause.setBorder(new LineBorder(Color.red , 20));
		pause.setMnemonic(KeyEvent.VK_SPACE);
		pause.setBorderPainted(false);
		// pause.setContentAreaFilled(false);
		pause.setPreferredSize(new Dimension(40, 35));

		play = new JButton();
		play.setIcon(new ImageIcon("img/royal/play.png"));
		play.setToolTipText("Play");
		play.setSelectedIcon(new ImageIcon("img/royal/play2.png"));
		play.setFocusable(false);
		play.setBorderPainted(false);
		play.setPreferredSize(new Dimension(40, 35));

		previous = new JButton();
		previous.setIcon(new ImageIcon("img/royal/previous.png"));
		previous.setToolTipText("Previous");
		previous.setSelectedIcon(new ImageIcon("img/royal/previous2.png"));
		previous.setFocusable(false);
		previous.setBorderPainted(false);
		previous.setPreferredSize(new Dimension(40, 35));

		next = new JButton();
		next.setIcon(new ImageIcon("img/royal/next.png"));
		next.setToolTipText("Next");
		next.setSelectedIcon(new ImageIcon("img/royal/next2.png"));
		next.setFocusable(false);
		next.setBorderPainted(false);
		next.setPreferredSize(new Dimension(40, 35));

		random = new JButton();
		random.setIcon(new ImageIcon("img/royal/random.png"));
		random.setToolTipText("Random");
		random.setSelectedIcon(new ImageIcon("img/royal/random2.png"));
		random.setFocusable(false);
		random.setBorderPainted(false);
		random.setPreferredSize(new Dimension(40, 35));

		open = new JButton();
		open.setIcon(new ImageIcon("img/royal/open.png"));
		open.setToolTipText("Open");
		open.setSelectedIcon(new ImageIcon("img/royal/open2.png"));
		open.setFocusable(false);
		open.setBorderPainted(false);
		open.setPreferredSize(new Dimension(40, 35));

		slider = new Slider(this);

		// slider.setForeground(Color.WHITE);

		BorderLayout b = new BorderLayout();
		b.setHgap(0);
		b.setVgap(0);
		this.setLayout(b);
		JPanel p1 = new JPanel();
		FlowLayout f = new FlowLayout();
		f.setHgap(0);
		f.setVgap(0);
		p1.setLayout(f);
		p1.setBackground(Color.WHITE);
		p1.setOpaque(true);
		// JPanel p2 = new JPanel();
		p1.add(stop);
		p1.add(pause);
		p1.add(play);
		p1.add(previous);
		p1.add(next);
		p1.add(random);
		// p1.add(open)

		// http://sirk.sytes.net/software/libs/kjdss/index.htm
		// -- Create a DSS.
		dss = new KJDigitalSignalSynchronizer();

		// -- Create DSP that comes with KJDSS (also used in KJ).
		wDsp = new KJScopeAndSpectrumAnalyzer();

		// -- Add DSP to DSS.
		dss.add(wDsp);

		wDsp.setPreferredSize(new Dimension(230, 80));

		dss.setSourceDataLineWriteEnabled(false);

		// -- Add DSP as component to JFrame

		// spectrum = new SpectrumTimeAnalyzer();
		// spectrum.setPreferredSize(new Dimension(230, 80));

		this.add(p1, BorderLayout.CENTER);
		this.add((Component) wDsp, BorderLayout.NORTH);
		this.add(slider, BorderLayout.SOUTH);

		this.addKeyListener(this);

		// leftPaneltop.setLayout(new BorderLayout());
		// leftPaneltop.add(spectrum, BorderLayout.SOUTH);
		// leftPaneltop.add(controlCenter, BorderLayout.NORTH);

		// Instantiate BasicPlayer.
		player = new BasicPlayer();
		// BasicPlayer is a BasicController.
		control = (BasicController) player;
		// Register BasicPlayerTest to BasicPlayerListener
		// events.
		// It means that this object will be notified on
		// BasicPlayer
		// events such as : opened(...), progress(...),
		// stateUpdated(...)
		player.addBasicPlayerListener(this);

		stop.addActionListener(new ActionListener() {

			public void actionPerformed(ActionEvent e) {
				try {
					control.stop();
					paused = false;
				} catch (BasicPlayerException e1) {
					// TODO Auto-generated catch block
					e1.printStackTrace();
				}
			}
		});

		pause.addActionListener(new ActionListener() {

			public void actionPerformed(ActionEvent e) {
				try {
					if (paused) {
						control.resume();
						paused = false;

					} else {
						paused = true;
						control.pause();
					}
					play.setFocusable(false);
					pause.setFocusable(true);
				} catch (BasicPlayerException e1) {
					// TODO Auto-generated catch block
					e1.printStackTrace();
				}
			}
		});

		play.addActionListener(new ActionListener() {

			public void actionPerformed(ActionEvent e) {

				if (paused) {
					try {
						control.resume();
					} catch (BasicPlayerException e1) {
						// TODO Auto-generated catch
						// block
						e1.printStackTrace();
					}
					paused = false;

				} else {
					detailsTable.play();
				}
				play.setFocusable(false);
				pause.setFocusable(true);
			}
		});
		play.setFocusable(true);

		previous.addActionListener(new ActionListener() {

			public void actionPerformed(ActionEvent e) {
				paused = false;
				detailsTable.previous();

			}
		});

		next.addActionListener(new ActionListener() {

			public void actionPerformed(ActionEvent e) {
				paused = false;
				detailsTable.next();

			}
		});

		random.addActionListener(new ActionListener() {

			public void actionPerformed(ActionEvent e) {
				paused = false;
				detailsTable.random();

			}
		});
	}

	public AudioFile getCurrentAudioFile() {

		return currentAudioFile;
	}

	public void setDetailsTable(DetailsTable detailsTable) {
		this.detailsTable = detailsTable;
	}

	public KJScopeAndSpectrumAnalyzer getKJScopeAndSpectrumAnalyzer() {
		return wDsp;
	}

	public void play(AudioFile audioFile) {

		lastFmSubmitted = false;
		new LastFmNowPlaying(audioFile.getArtist(), audioFile.getTitle())
				.start();

		try {
			// Open file, or URL or Stream (shoutcast) to
			// play.
			control.open(audioFile.getFile());
			// control.open(new
			// URL("http://yourshoutcastserver.com:8000"));
			// Start playback in a thread.
			this.currentAudioFile = audioFile;
			control.play();

			slider.setMaximum((int) audioFile.getPlayTime());
			slider.setValue(0);

			detailsTable.handleCurrentPlaying(audioFile);

			// currentAudioFile.get
			// System.out.println((int) currentAudioFile.getPlayTime()
			// * currentAudioFile.getSamplingRate() + "sampling rate : "
			// + currentAudioFile.getSamplingRate());
			// Set Volume (0 to 1.0).
			// setGain should be called after
			// control.play(). 0.85
			control.setGain(1.0);
			// Set Pan (-1.0 to 1.0).
			// setPan should be called after control.play().
			control.setPan(0.0);
			// If you want to pause/resume/pause the played
			// file then
			// write a Swing player and just call
			// control.pause(),
			// control.resume() or control.stop().
			// Use control.seek(bytesToSkip) to seek file
			// (i.e. fast forward and rewind). seek feature
			// will
			// work only if underlying JavaSound SPI
			// implements
			// skip(...). True for MP3SPI (JavaZOOM) and SUN
			// SPI's
			// (WAVE, AU, AIFF).

			// slider.setMaximum((int) currentAudioFile.getPlayTime()
			// * currentAudioFile.getBitRate() / 1000);

		} catch (BasicPlayerException e) {
			e.printStackTrace();
		}
	}

	protected void processSeek(double rate) {
		lastFmSubmitted = true; // we disable submission for this track
		try {
			if ((audioInfo != null) && (audioInfo.containsKey("audio.type"))) {
				String type = (String) audioInfo.get("audio.type");
				// Seek support for MP3.
				if ((type.equalsIgnoreCase("mp3"))
						&& (audioInfo.containsKey("audio.length.bytes"))) {
					// long skipBytes = (long) Math.round(((Integer) audioInfo
					// .get("audio.length.bytes")).intValue()
					// * rate);
					long skipBytes = (long) Math.round(((Integer) audioInfo
							.get("audio.length.bytes")).intValue()
							* rate / currentAudioFile.getPlayTime());

					control.seek(skipBytes);
				}
				// Seek support for WAV.
				else if ((type.equalsIgnoreCase("wave"))
						&& (audioInfo.containsKey("audio.length.bytes"))) {
					long skipBytes = (long) Math.round(((Integer) audioInfo
							.get("audio.length.bytes")).intValue()
							* rate);
					control.seek(skipBytes);
				}

			}
		} catch (BasicPlayerException ioe) {

		}
	}

	/**
	 * Open callback, stream is ready to play.
	 * 
	 * properties map includes audio format dependant features such as bitrate,
	 * duration, frequency, channels, number of frames, vbr flag, id3v2/id3v1
	 * (for MP3 only), comments (for Ogg Vorbis), ...
	 * 
	 * @param stream
	 *            could be File, URL or InputStream
	 * @param properties
	 *            audio stream properties.
	 */
	public void opened(Object stream, Map properties) {
		// Pay attention to properties. It's useful to get
		// duration,
		// bitrate, channels, even tag such as ID3v2.
		audioInfo = properties;
		display("opened : " + properties.toString());
	}

	/**
	 * Progress callback while playing.
	 * 
	 * This method is called severals time per seconds while playing. properties
	 * map includes audio format features such as instant bitrate, microseconds
	 * position, current frame number, ...
	 * 
	 * @param bytesread
	 *            from encoded stream.
	 * @param microseconds
	 *            elapsed (<b>reseted after a seek !</b>).
	 * @param pcmdata
	 *            PCM samples.
	 * @param properties
	 *            audio stream parameters.
	 */
	private long secondsAmount = 0;
	private long oldSecondsAmount = 0;

	// private static final int READ_BUFFER_SIZE = 1024 * 4;

	public void progress(int bytesread, long microseconds, byte[] pcmdata,
			Map properties) {
		// System.out.println(pcmdata.length);
		// display("progress : " + properties.toString());
		int byteslength = -1;
		long total = -1;
		// Try to get time from playlist item.
		if (currentAudioFile != null)
			total = currentAudioFile.getPlayTime();
		// If it fails then try again with JavaSound SPI.
		if (total <= 0)
			total = (long) Math
					.round(getTimeLengthEstimation(audioInfo) / 1000);
		// If it fails again then it might be stream => Total = -1
		if (total <= 0)
			total = -1;
		if (audioInfo.containsKey("basicplayer.sourcedataline")) {
			// Spectrum/time analyzer

			// -- Allocate a read buffer.
			// byte[] wRb = new byte[ READ_BUFFER_SIZE ];
			// int wRs = 0;
			//        	
			// pConsumer.writeAudioData(
			// sampleBuffer.getBuffer(),
			// 0,
			// sampleBuffer.getBufferLength() );

			// -- Read from WAV file and write to DSS (and the monitored source
			// data line)
			// while( ( wRs = wAs.read( wRb ) ) != -1 ) {
			// dss.writeAudioData( wRb, 0, wRs );
			// }

			if (dss != null && (!isIconified())) {
				dss.writeAudioData(pcmdata);
			}
		}
		if (audioInfo.containsKey("audio.length.bytes")) {
			byteslength = ((Integer) audioInfo.get("audio.length.bytes"))
					.intValue();
		}
		float progress = -1.0f;
		if ((bytesread > 0) && ((byteslength > 0)))
			progress = bytesread * 1.0f / byteslength * 1.0f;
		if (total > 0)
			secondsAmount = (long) (total * progress);
		else
			secondsAmount = -1;

		if (!(lastFmSubmitted) && (secondsAmount > 240 || progress > 0.5)) {

			lastFmSubmitted = true;
			new LastFmSubmit(currentAudioFile.getArtist(), currentAudioFile
					.getTitle(), currentAudioFile.getAlbum(), (int) total,
					currentAudioFile.getTrack(), Source.USER, (System
							.currentTimeMillis() / 1000)
							- secondsAmount).start();

		}

		if (total != 0) {
			// if (posValueJump == false)
			// {
			// System.out.println(secondsAmount + " - " + total);

			if (secondsAmount == 0) {
				slider.setValue((int) secondsAmount);
			} else {
				if (oldSecondsAmount != secondsAmount) {
					slider.setValue((int) secondsAmount);
				}
			}

			// long skipBytes = (long) Math.round(((Integer)
			// audioInfo.get("audio.length.bytes")).intValue() * rate);
			// }
		} else {
			slider.setValue(0);
		}
		// String time = FileUtil.getFormatedDuration(Math.round(microseconds/
		// (int) Math.pow(10, 6)));
		if (oldSecondsAmount != secondsAmount) {
			String time = FileUtil.getFormatedDuration(secondsAmount);
			setTitle(time);
		}
		// spectrum.setTime(time);
		oldSecondsAmount = secondsAmount;
	}

	/**
	 * Notification callback for basicplayer events such as opened, eom ...
	 * 
	 * @param event
	 */
	public void stateUpdated(BasicPlayerEvent event) {
		// Notification of BasicPlayer states (opened, playing,
		// end of media, ...)
		display("stateUpdated : " + event.toString());
		int state = event.getCode();
		if (state == BasicPlayerEvent.STOPPED) {
			System.out.println("stopped");
		}

		if (state == BasicPlayerEvent.EOM) {
			if (!(paused)) {
				detailsTable.next();
			}
			// if ((playerState == PAUSE) || (playerState == PLAY))
			// {
			// playlist.nextCursor();
			// playlistUI.nextCursor();
			// PlaylistItem pli = playlist.getCursor();
			// setCurrentSong(pli);
			// }
		} else if (state == BasicPlayerEvent.PLAYING) {
			// lastScrollTime = System.currentTimeMillis();
			// posValueJump = false;
			if (audioInfo.containsKey("basicplayer.sourcedataline")) {
				if (dss != null) {
					dss.start((SourceDataLine) audioInfo
							.get("basicplayer.sourcedataline"));
					// dss.startDSP((SourceDataLine) audioInfo
					// .get("basicplayer.sourcedataline"));
				}
			}
		} else if (state == BasicPlayerEvent.SEEKING) {
			// posValueJump = true;
		} else if (state == BasicPlayerEvent.SEEKED) {

		} else if (state == BasicPlayerEvent.OPENING) {

		} else if (state == BasicPlayerEvent.STOPPED) {
			if (dss != null) {
				dss.stop();
				// dss.repaint();
			}
		}

	}

	/**
	 * A handle to the BasicPlayer, plugins may control the player through the
	 * controller (play, stop, ...)
	 * 
	 * @param controller
	 *            : a handle to the player
	 */
	public void setController(BasicController controller) {
		display("setController : " + controller);
	}

	public void display(String msg) {
		if (out != null)
			out.println(msg);
	}

	public void setTitle(String time) {

		window.setTitle(currentAudioFile.getArtist() + " - "
				+ currentAudioFile.getTitle() + "     " + time + "/"
				+ currentAudioFile.getFormatedDuration());

	}

	/**
	 * Try to compute time length in milliseconds.
	 * 
	 * @param properties
	 * @return
	 */
	public long getTimeLengthEstimation(Map properties) {
		long milliseconds = -1;
		int byteslength = -1;
		if (properties != null) {
			if (properties.containsKey("audio.length.bytes")) {
				byteslength = ((Integer) properties.get("audio.length.bytes"))
						.intValue();
			}
			if (properties.containsKey("duration")) {
				milliseconds = (int) (((Long) properties.get("duration"))
						.longValue()) / 1000;
			} else {
				// Try to compute duration
				int bitspersample = -1;
				int channels = -1;
				float samplerate = -1.0f;
				int framesize = -1;
				if (properties.containsKey("audio.samplesize.bits")) {
					bitspersample = ((Integer) properties
							.get("audio.samplesize.bits")).intValue();
				}
				if (properties.containsKey("audio.channels")) {
					channels = ((Integer) properties.get("audio.channels"))
							.intValue();
				}
				if (properties.containsKey("audio.samplerate.hz")) {
					samplerate = ((Float) properties.get("audio.samplerate.hz"))
							.floatValue();
				}
				if (properties.containsKey("audio.framesize.bytes")) {
					framesize = ((Integer) properties
							.get("audio.framesize.bytes")).intValue();
				}
				if (bitspersample > 0) {
					milliseconds = (int) (1000.0f * byteslength / (samplerate
							* channels * (bitspersample / 8)));
				} else {
					milliseconds = (int) (1000.0f * byteslength / (samplerate * framesize));
				}
			}
		}
		return milliseconds;
	}

	public void keyPressed(KeyEvent e) {
		int touche = e.getKeyCode();
		if ((touche == KeyEvent.VK_ENTER) || (touche == KeyEvent.VK_SPACE)) {
			try {
				if (paused) {

					control.resume();

					paused = false;
				} else {
					control.play();
				}

			} catch (BasicPlayerException e1) {
				// TODO Auto-generated catch block
				e1.printStackTrace();
			}

		}
	}

	public void keyReleased(KeyEvent e) {
	}

	public void keyTyped(KeyEvent e) {
	}

	public PlaylistListModel getPlaylistListModel() {
		return playlistListModel;
	}

	public void setPlaylistListModel(PlaylistListModel playlistListModel) {
		this.playlistListModel = playlistListModel;
	}

	class LastFmSubmit extends Thread {
		String artist;
		String title;
		String album;
		int length;
		int tracknumber;
		Source source;
		long startTime;

		public LastFmSubmit(String artist, String title, String album,
				int length, int tracknumber, Source source, long startTime) {
			this.artist = artist;
			this.title = title;
			this.album = album;
			this.length = length;
			this.tracknumber = tracknumber;
			this.source = source;
			this.startTime = startTime;
		}

		public void run() {
			String LastFmLogin = Preferences.getInstance().getLastFmLogin();
			String LastFmPassword = Preferences.getInstance()
					.getLastFmPassword();
			if (!LastFmLogin.equals("") && !LastFmPassword.equals("")) {
				try {

					if (scrobbler == null) {
						scrobbler = Scrobbler.newScrobbler("tst", "1.0",
								LastFmLogin);

						status = scrobbler.handshake(LastFmPassword);
						System.out.println("LastFM handshake ok: "
								+ status.ok());
					}

					scrobbler.submit(artist, title, album, length, tracknumber,
							source, startTime);

					System.out.println("LastFM submission: " + status.ok());
				} catch (IOException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				} // see javadoc for parameter explanation

			}
		}
	}

	class LastFmNowPlaying extends Thread {
		String artist;
		String title;

		public LastFmNowPlaying(String artist, String title) {
			this.artist = artist;
			this.title = title;
		}

		public void run() {
			String LastFmLogin = Preferences.getInstance().getLastFmLogin();
			String LastFmPassword = Preferences.getInstance()
					.getLastFmPassword();
			if (!LastFmLogin.equals("") && !LastFmPassword.equals("")) {

				try {
					if (scrobbler == null) {
						scrobbler = Scrobbler.newScrobbler("tst", "1.0",
								LastFmLogin);

						status = scrobbler.handshake(LastFmPassword);
						System.out.println("LastFM handshake ok: "
								+ status.ok());
					}

					status = scrobbler.nowPlaying(artist, title);
					System.out.println("LastFM now playing ok: " + status.ok());
				} catch (IOException e) {

				}
			}
		}
	}
}
